/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.editor.model;

import java.awt.Rectangle;

import javax.swing.JEditorPane;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.event.DocumentEvent;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import offset.nodes.client.editor.Editor;
import offset.nodes.client.editor.controller.ViewActions;
import offset.nodes.client.editor.view.PopupTree;

public abstract class ElementTree extends PopupTree {

    JEditorPane pane;

    public ElementTree(JEditorPane pane, JTree tree, Editor editor) {
        super(tree, editor);
        this.pane = pane;
        this.popup = createPopupMenu(editor);
    }   

    public void scrollToDocumentPosition(int pos) throws BadLocationException {
        Rectangle r = pane.modelToView(pos);
        if (r != null) {
            if (r.width == 0) {
                r.width = 1;
            }
            // the view is visible, scroll it to the 
            // center of the current visible area.
            Rectangle vis = pane.getVisibleRect();
            if (!vis.contains(r)) {
                r.height = vis.height;
                pane.scrollRectToVisible(r);
            }
        }
    }

    public JEditorPane getPane() {
        return pane;
    }

    public void update() {
        ((DefaultTreeModel) tree.getModel()).setRoot(createTree());
        pane.getDocument().addDocumentListener(this);
        refresh();
    }
    
    public void detachFromDocument() {
        pane.getDocument().removeDocumentListener(this);
    }
    
    protected boolean elementEquals(Element element, DefaultMutableTreeNode treeNode) {
        return element.getName().equals(getName(treeNode));
    }

    public void updateTree(DocumentEvent.EventType type, DefaultMutableTreeNode parentNode, DefaultMutableTreeNode newParentNode) {
        if (parentNode.getChildCount() == 0) {
            if (newParentNode.getChildCount() > 0) {
                int count = newParentNode.getChildCount();
                for (int i = 0; i < count; i++) {
                    ((DefaultTreeModel) tree.getModel()).insertNodeInto((DefaultMutableTreeNode) newParentNode.getChildAt(0), parentNode, i);
                }
            }
            return;
        }

        DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) parentNode.getFirstChild();
        int e = 0;
        int n = 0;
        while (childNode != null) {
            if (newParentNode.getChildCount() < e + 1) {
                DefaultMutableTreeNode oldNode = childNode;
                childNode = childNode.getNextSibling();
                ((DefaultTreeModel) tree.getModel()).removeNodeFromParent(oldNode);
                continue;
            }
            DefaultMutableTreeNode newChildNode = (DefaultMutableTreeNode) newParentNode.getChildAt(e);

            if (!(childNode.getUserObject()).equals(newChildNode.getUserObject())) {
                Element before = (Element) childNode.getUserObject();
                if (elementEquals(before, newChildNode)) {
                    childNode.setUserObject(newChildNode.getUserObject());
                    ((DefaultTreeModel) tree.getModel()).reload(childNode);
                } else {
                    if (type == DocumentEvent.EventType.INSERT) {
                        DefaultMutableTreeNode newNode = newChildNode;
                        ((DefaultTreeModel) tree.getModel()).insertNodeInto(newNode,
                                parentNode, n);
                        continue;
                    } else if (type == DocumentEvent.EventType.REMOVE) {
                        DefaultMutableTreeNode oldNode = childNode;
                        childNode = childNode.getNextSibling();
                        ((DefaultTreeModel) tree.getModel()).removeNodeFromParent(oldNode);
                        continue;
                    }
                }
            }

            updateTree(type, childNode, newChildNode);

            e++;
            n++;
            childNode = childNode.getNextSibling();
        }

        while (e < newParentNode.getChildCount()) {
            DefaultMutableTreeNode newChildNode = (DefaultMutableTreeNode) newParentNode.getChildAt(e);
            DefaultMutableTreeNode newNode = newChildNode;
            ((DefaultTreeModel) tree.getModel()).insertNodeInto(newNode,
                    parentNode, n);

            n++;
        }
    }
    
    public void selectElement(Element e) {
        DefaultMutableTreeNode node = findNode(e);
        if ( node == null) // shouldnot happen
            return;
        
        TreePath path = new TreePath(node.getPath());
        tree.setSelectionPath(path);
        tree.scrollPathToVisible(path);       
    }

    protected void gotoNode() {
        if (tree.getSelectionPath() == null) {
            return;
        }

        DefaultMutableTreeNode remove = (DefaultMutableTreeNode) tree.getSelectionPath().getLastPathComponent();

        Element element = (Element) remove.getUserObject();

        try {
            scrollToDocumentPosition(element.getStartOffset());
            pane.setCaretPosition(element.getStartOffset());
            pane.grabFocus();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    protected JPopupMenu createPopupMenu(Editor editor) {
        JPopupMenu menu = new JPopupMenu();

        JMenuItem selectMenuItem = new JMenuItem();
        selectMenuItem.setAction(new ViewActions.SelectElementAction(pane, tree, editor));
        selectMenuItem.setText(java.util.ResourceBundle.getBundle("offset/nodes/client/editor/view/resources/EditorBundle").getString("SelectElement"));
        menu.add(selectMenuItem);

        return menu;
    }

    protected void defaultAction() {
        gotoNode();
    }
}
